package slap.app;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jodd.format.Printf;
import jodd.io.findfile.ClasspathScanner;
import jodd.util.ReflectUtil;
import jodd.util.StringUtil;
import jregex.Pattern;

import slap.PropsNames;
import slap.Resources;
import slap.Slap;
import slap.annotation.Accept;
import slap.exception.SlapException;
import slap.http.Request;
import slap.http.Response;
import slap.mvc.ActionInvoker;
import slap.mvc.DefaultHttpRequestController;
import slap.mvc.HttpRequestController;
import slap.mvc.Action;
import slap.mvc.WebSocketHandler;
import slap.mvc.router.DirectRouter;
import slap.mvc.router.RegexRouter;
import slap.mvc.router.Router;
import slap.mvc.router.WildcardRouter;
import slap.util.Asserts;

public abstract class Application {
	
	protected static final Logger logger = LoggerFactory.getLogger(Application.class);
	
	private final String PROPS_DEFAULT = 
			Resources.Presets.DIR_ETC+"app-"+getAppName()+".conf";
	
	public final String realPath = Resources.Presets.DIR_STATIC;
	public static Properties props;
	
	public Map<Class<?>, HttpRequestController> httpRequestControllers = new HashMap<Class<?>, HttpRequestController>();
	public Map<String, WebSocketHandler> webSocketHandlers = new HashMap<String, WebSocketHandler>();
	
	public String staticDir = Resources.Presets.DIR_STATIC+getAppName();
	
	public String domain;
	
	public String url;
	
	public Router directRouter = new DirectRouter();
	public Router wildcardRouter = new WildcardRouter();
	public Router regexRouter = new RegexRouter();
	
	public Map<String, WebSocketHandler> webSocketHandlersMapping = new HashMap<String, WebSocketHandler>();
	
	public boolean debug;
	
	public Application(){
		props = loadProps();
		debug = Boolean.valueOf(props.getProperty("application.debug", "false"));
		String domainRaw = props.getProperty(PropsNames.SERVER_DOMAIN);
		if(StringUtil.isNotBlank(domainRaw)){
			domainRaw = domainRaw.trim();
			if(domainRaw.equals(".")){
				domain = Slap.baseDomain;
			}else if(domainRaw.endsWith(".")){
				domain = domainRaw + Slap.baseDomain;
			}else{
				domain = domainRaw;
			}
		}else{
			logger.error("No http.server config or it is blank");
		}
		if(Boolean.valueOf(props.getProperty(PropsNames.SERVER_DEFAULT, "false"))){
			Asserts.Null(Slap.defaultApplication);
			Slap.defaultApplication = this;
		}
		try{
			scan();
		}catch(Exception e){
			e.printStackTrace();
		}
		if(Slap.port == 80){
			url = Printf.str("http://%s", this.domain);
		}else{
			url = Printf.str("http://%s:%d", this.domain, Slap.port);
		}
		logger.info(url);
	}
	
	public abstract String getAppName();
	
	
	public void scan(){
		DefaultHttpRequestController dc = new DefaultHttpRequestController();
		try {
			ActionParser.parse(dc.getClass().getMethod("dedicated_resource"), this);
			ActionParser.parse(dc.getClass().getMethod("shared_resource"), this);
			ActionParser.parse(dc.getClass().getMethod("favicon"), this);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		
		
		for(Method method : Resources.ManagedMethods.withAnnotation(Accept.class)){
			Class<?> clazz = method.getDeclaringClass();
			if(clazz.getName().startsWith("app."+this.getAppName())){
				ActionParser.parse(method, this);
			}
		}
	}
	
	
	public File getFile(String name){
		File file = new File(realPath+name);
		return file.exists() ? file : null;
	}
	
	protected Properties loadProps(){
		Properties props = new Properties();
		try {
			props.load(
				new FileInputStream(PROPS_DEFAULT)	
			);
		} catch (IOException e) {
			e.printStackTrace();
		}
		return props;
	}
	

}
